#!/usr/bin/python
# File: processing_benchmark.py
# Author: Miguel Sousa Filipe
# Desc: This program demonstrates parallel computations with processing module
# It calculates the sum of prime numbers below a given integer in parallel
from processing import Process, Queue
import time
import math
import sys

work_counter = 0
worker_queue = [] 
workers = [] 
 
def setup_workers(nworkers, exec_fn):
    i = 0
    while  i < nworkers:
        worker_queue.append(Queue())
        workers.append( Process(target=exec_fn, args=[worker_queue[i]]) )
        workers[i].start()
        i += 1

def submit_work(*args):
    global work_counter
    worker_queue[ work_counter % len(worker_queue) ].put(*args)
    work_counter += 1

def isprime(n):
    """Returns True if n is prime and False otherwise"""
    if not isinstance(n, int):
        raise TypeError("argument passed to is_prime is not of 'int' type")
    if n < 2:
        return False
    if n == 2:
        return True
    max = int(math.ceil(math.sqrt(n)))
    i = 2
    while i <= max:
        if n % i == 0:
            return False
        i += 1
    return True

def sum_primes(n):
    """Calculates sum of all primes below given integer n"""
    return sum([x for x in xrange(2,n) if isprime(x)])


def worker_run(q):
    while 1:
        x = q.get()
        print "Sum of primes below", x, "is", sum_primes(x)
        if(q.empty()): break

    

start_time = time.time()

inputs = xrange(100000, 1000000, 10000)


if len(sys.argv) > 1:
    ncpus = int(sys.argv[1])
else:
    ncpus = len(inputs)
print "Starting procesing with ",ncpus, "workers"

#create workers and queues for them
setup_workers(ncpus, worker_run)

#dispatch work 
for x in inputs:
    submit_work(x)

#wait for all to finish
for w in workers:
    w.join()


print "Time elapsed: ", time.time() - start_time, "s"
